﻿using System;
using System.ComponentModel;
using System.Reflection;
using Castle.Core.Interceptor;
using FluentSilverlight.Commands;
using FluentSilverlight.Utility.Reflection;

namespace FluentSilverlight.MVVM
{
    public class AutoNotifyPropertyChangedInterceptor<TModel> : IInterceptor
        where TModel : IAutoNotifyPropertyChanged
    {
        private const string SET_PREFIX = "set_";

        public void Intercept(IInvocation invocation)
        {
            var methodInfo = invocation.Method;

            invocation.Proceed();

            if (ShouldSuppressNotifyChanged(methodInfo)) return;

            var model = ((TModel)invocation.Proxy);

            model.OnPropertyChanged(methodInfo.Name.Substring(SET_PREFIX.Length));
        }

        private static bool ShouldSuppressNotifyChanged(MethodInfo methodInfo)
        {
            if (!methodInfo.IsSpecialName || !methodInfo.Name.StartsWith(SET_PREFIX)) return true;

            if (IsDecoratedWithDoNotNotifyChangesAttribute(methodInfo)) return true;

            return IsFsCommand(methodInfo);
        }

        private static bool IsFsCommand(MethodInfo methodInfo)
        {
            var pi = GetPropertyInfoForSetterMethod(methodInfo);
            return (pi != null && typeof(IFsCommand).IsAssignableFrom(pi.PropertyType));
        }

        private static bool IsDecoratedWithDoNotNotifyChangesAttribute(MethodInfo methodInfo)
        {
            var pi = GetPropertyInfoForSetterMethod(methodInfo);
            return (pi != null && pi.GetAttribute<DoNotNotifyChangesAttribute>() != null);
        }

        private static PropertyInfo GetPropertyInfoForSetterMethod(MethodInfo methodInfo)
        {
            var propertyName = methodInfo.Name.Substring(SET_PREFIX.Length);
            return methodInfo.DeclaringType.GetProperty(propertyName);
        }
    }
}
